﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LitJson;

[AddComponentMenu("PUEX/Affector/Physics/BoxCollider")]
public class PUEXBoxColliderAffector : PUEXBaseColliderAffector {

	#region - Editable property
	[Header("BoxCollider:")]
	public Vector3 center;
	public string centerParamName = "";
	public Vector3 size;
	public string sizeParamName = "";
	public bool innerCollision;
	#endregion

	#region - Property
	protected Bounds _colliderBox;
	#endregion
	
	#region - Lifecycle
	public PUEXBoxColliderAffector () : base()
	{
		center = Vector3.zero;
		size = new Vector3(20f, 20f, 20f);
		innerCollision = false;
	}
	#endregion

	#region - Update
	public override void preUpdate (float dt)
	{
		base.preUpdate (dt);

		Vector3 center_ = center;
		Vector3 size_ = size;
		if (inWorldSpace) {
			if (ownerEmitter.keepLocal) {
				Matrix4x4 invMat = ownerEmitter.ownerParticleSystem.systemToWorldTransform.inverse;
				center_ = invMat.MultiplyPoint(center_);
				Vector3 s = ownerEmitter.ownerParticleSystem.systemToWorldScale3;
				s = new Vector3 (1f / s.x, 1f / s.y, 1f / s.z);
				size_.Scale(s);
			}
		} else {
			if (!ownerEmitter.keepLocal) {
				center_ = ownerEmitter.ownerParticleSystem.systemToWorldTransform.MultiplyPoint(center_);
				size_.Scale(ownerEmitter.ownerParticleSystem.systemToWorldScale3);
			}
		}
		
		_colliderBox = new Bounds (center_, size_);
	}

	public override void update (float dt, PUEXParticle particle)
	{
		Vector3 predictedPosition = particle.position + particle.velocity() * dt;
		bool collision = false;

		switch (intersectionType) {
		case IntersectionType.IT_SPHERE:
			{
				Sphere particleColliderSphere = new Sphere (predictedPosition, particle.boundingSphereRadius());

				// if (PUEXUtils.intersects(particleColliderSphere, _colliderBox) && innerCollision == false)
				// OR
				// if (!PUEXUtils.intersects(particleColliderSphere, _colliderBox) && innerCollision == true)
				if (PUEXUtils.intersects (particleColliderSphere, _colliderBox) != innerCollision)
					collision = true;

				break;
			}
		case IntersectionType.IT_BOX:
			{
				Bounds particleColliderBox = new Bounds (predictedPosition, particle.dimensions ());
			
				// if (_colliderBox.Intersects(particleColliderBox) && innerCollision == false)
				// OR
				// if (!_colliderBox.Intersects(particleColliderBox) && innerCollision == true)
				if (_colliderBox.Intersects (particleColliderBox) != innerCollision)
					collision = true;

				break;
			}
		}

		if (collision)
		{
			particle.addEventFlags(PUEXParticle.PEF_COLLIDED);

			if (minKillSpeed > 0f && Mathf.Abs(particle.speed) <= minKillSpeed) {
				particle.kill();
			}

			if (minFreezeSpeed > 0f && !particle.freezed && Mathf.Abs(particle.speed) <= minFreezeSpeed) {
				particle.freeze ();
			}

			if (particle.timeToLive > 0f && !particle.freezed) {
				switch (collisionType) {
				case CollisionType.CT_BOUNCE:
				{
					// Determine the nearest side and reverse the direction
					if (isSmallestValue (particle.position.x - _colliderBox.min.x, particle.position)) {		
						particle.direction.x *= -1f;
					} else if (isSmallestValue (_colliderBox.max.x - particle.position.x, particle.position)) {
						particle.direction.x *= -1f;
					} else if (isSmallestValue (particle.position.y - _colliderBox.min.y, particle.position)) {
						particle.direction.y *= -1f;
					} else if (isSmallestValue (_colliderBox.max.y - particle.position.y, particle.position)) {
						particle.direction.y *= -1f;
					} else if (isSmallestValue (particle.position.z - _colliderBox.min.z, particle.position)) {
						particle.direction.z *= -1f;
					} else if (isSmallestValue (_colliderBox.max.z - particle.position.z, particle.position)) {
						particle.direction.z *= -1f;
					}
					particle.direction.Normalize();
					particle.speed *= bouncyness;
					
					break;
				}
				case CollisionType.CT_FLOW:
				{
					if (isSmallestValue (particle.position.x - _colliderBox.min.x, particle.position)) {		
						particle.direction.x = 0f;
					} else if (isSmallestValue (_colliderBox.max.x - particle.position.x, particle.position)) {
						particle.direction.x = 0f;
					} else if (isSmallestValue (particle.position.y - _colliderBox.min.y, particle.position)) {
						particle.direction.y = 0f;
					} else if (isSmallestValue (_colliderBox.max.y - particle.position.y, particle.position)) {
						particle.direction.y = 0f;
					} else if (isSmallestValue (particle.position.z - _colliderBox.min.z, particle.position)) {
						particle.direction.z = 0f;
					} else if (isSmallestValue (_colliderBox.max.z - particle.position.z, particle.position)) {
						particle.direction.z = 0f;
					}
					particle.direction.Normalize();
					particle.speed *= friction;
					
					break;
				}
				}

				calculateRotationSpeedAfterCollision(particle);
			}
		}
	}

	protected bool isSmallestValue (float value, Vector3 particlePosition)
	{
		float value1 = particlePosition.x - _colliderBox.min.x;
		float value2 = _colliderBox.max.x - particlePosition.x;
		float value3 = particlePosition.y - _colliderBox.min.y;
		float value4 = _colliderBox.max.y - particlePosition.y;
		float value5 = particlePosition.z - _colliderBox.min.z;
		float value6 = _colliderBox.max.z - particlePosition.z;
		
		return (
			value <= value1 && 
			value <= value2 &&
			value <= value3 && 
			value <= value4 &&
			value <= value5 &&
			value <= value6);
	}
	#endregion

	#region - Param property
	public override void updateParams (PUEXParticleSystem system)
	{
		Vector3 value;
		if (system.getParamVec3 (centerParamName, out value)) {
			center = value;
		}
		if (system.getParamVec3 (sizeParamName, out value)) {
			size = value;
		}
	}
	#endregion

	#region - Draw
	public void OnDrawGizmos()
	{
		if (!enabled)
			return;

		Matrix4x4 defaultMatrix = Gizmos.matrix;
		if (inWorldSpace) {
			Gizmos.matrix = Matrix4x4.identity;
		} else {
			Gizmos.matrix = transform.parent.transform.localToWorldMatrix;
		}
		
		Color defaultColor = Gizmos.color;
		Gizmos.color = new Color(Color.yellow.r, Color.yellow.g, Color.yellow.b, 0.3f);
		
		Gizmos.DrawCube (center, size);
		
		Gizmos.color = defaultColor;
		Gizmos.matrix = defaultMatrix;
	}
	#endregion

	#region - Serialization
	public override JsonData save (JsonData emitterData, JsonData systemData)
	{
		JsonData ret = base.save (emitterData, systemData);

		ret ["center"] = PUEXUtils.serializeVector3(center);
		ret ["center_param_name"] = centerParamName;
		ret ["size"] = PUEXUtils.serializeVector3(size);
		ret ["size_param_name"] = sizeParamName;
		ret ["inner_collision"] = innerCollision;
		
		return ret;
	}
	#endregion

}
